package cosc617_blackteam;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Laura
 * This class is responsible for running the simulation.
 */
public class RestaurantSim extends ObSubject implements Runnable {

    private Boolean running; //whether program is running
    private long clockTick; //representing a unit of time
    //put a max clock ticks so that we can have a standard 
    //length of time for comparison of different strategies
    private static final long MAX_CLOCK_TICKS = 20000;
    private static final int MAX_PARTY_SIZE = 6;
    private ArrayList<CustomerParty> customers;
    private int frequency;
    private int sleepTime;
    private IdGenerator idGen;
    private SystemInterface systemInterface;
    private boolean resetSim;
    private IdGenerator partyIdGen;
    //pre generated comments
    private String[] comments={"Meal was good", "Service was slow", "Delicious!",
    		"Automated restaurant is a cool feature", "Food was bland"};
    //pre generated names
    private String[] names = {"Robert", "Andy", "Lisa", "Clementine", "George",
    		"Dean", "Sam", "Bobbly"};
 

    /**
     * @param systemInterface the systemInterface that contains the correct
     * reference to the infoaggregator
     */
    public RestaurantSim(SystemInterface systemInterface) {
        running = false;
        clockTick = 0;
        customers = new ArrayList<CustomerParty>();
        idGen = new IdGenerator(0);
        partyIdGen = new IdGenerator(1);
        this.systemInterface = systemInterface;
        setFrequency(7);

        this.sleepTime = 50;
        resetSim = false;
    }

    /**
     * To allow reset from this thread instead of main thread
     *
     * @param resetSim
     */
    public void setResetSim(Boolean resetSim) {
        this.resetSim = resetSim;
    }

    /**
     * Resets clock ticks, empties customer queue tells the system interface to
     * reset its stats such as number served, setting tables as empty
     */
    private void resetSimulation() {
        resetSim = false;
        clockTick = 0;
        systemInterface.resetStatistics();
        customers = new ArrayList<CustomerParty>();
    }

    /**
     * @param frequency An integer between 1 and 10 where 1 is slow customer
     * flow and 10 is high numbers out of range will be set to 1 or 10.
     */
    public void setFrequency(int frequency) {
        int x = frequency;
        if (x < 1) {
            x = 1;
        } else if (x > 10) {
            x = 10;
        }
        //the actual frequency seeing if the clock tick is divisible
        //by the frequency so a lower number will actually be faster 
        this.frequency = 11 - x;

    }

    public void run() {

        while (true) {
            //if the program has been toggled to stop or if the specified number
            //of clock ticks has occurred then yield thread back to main program.
            if (resetSim) {
                resetSimulation();
            }
            while (!running || clockTick > MAX_CLOCK_TICKS) {
                if (resetSim) {
                    resetSimulation();
                }
                Thread.yield();
            }

            advanceClock();
            //if clock tick is divisible by frequency  and not too many in queue add another party
            if (clockTick % frequency == 0 && customers.size() < 200) {
                addParty();
            }
            systemInterface.cookFood();
            for (int i = 0; i < customers.size(); i++) {
                try {
                    Thread.sleep(this.sleepTime);
                } catch (InterruptedException ex) {
                    Logger.getLogger(RestaurantSim.class.getName()).log(Level.SEVERE, null, ex);
                }
                CustomerParty party = customers.get(i);

                
                if (party.needsSeating() && systemInterface.addParty(party)) {
                    party.assignToTable();
                    notifyObservers("Party id " + party.id + " assigned to table");
                    generatePartyOrders(party);

                }
                
                
                party.deliverFood();
                party.doEating();
                
                if (party.unseatCustomer()) {
                    systemInterface.generateTab(party);
                    systemInterface.removeParty(party);
                    if((int)(Math.random()*11) == 10) //randomly add comment
                    	createComment();
                    systemInterface.removeOrders(party.getPartyOrders());
                    customers.remove(i);
                    notifyObservers("Party id " + party.id + " has left");
                }
                
            }
        }
    }

    /**
     * @return a string array identifying parties waiting in the queue
     */
    public ArrayList<String> getWaitingCustomers() {
        ArrayList<String> output = new ArrayList<String>();
        for (CustomerParty party : customers) {
            if (party.needsSeating()) {
                output.add(party.toString());
            }
        }
        return output;
    }

    /**
     * @return the number of clock ticks that has passed in the simulation
     */
    public long getClockTick() {
        return clockTick;
    }

    /**
     * Creates a new party with 1 - MAX_PARTY_SIZE customers
     */
    private void addParty() {
        CustomerParty party = new CustomerParty(partyIdGen.getNextId());
        int numCustomers = (int) (Math.random() * ((MAX_PARTY_SIZE))) + 1;
        for (int i = 1; i <= numCustomers; i++) {
            party.addCustomer(new Customer(idGen.getNextId()));
        }
        customers.add(party);
        notifyObservers("Party of size " + party.getSize() + " added to Queue id = " +party.getID());
     
    }

    public void generatePartyOrders(CustomerParty party) {
        ArrayList<String> menu = systemInterface.getDefaultMenu();
        notifyObservers("\n ------------------------------ \nOrder For Party #" + party.id);
        for (int numCust = 0; numCust < party.getSize(); numCust++) {
            

            int numItemsToOrder = 1;//(int)(Math.random()*3)+1; setting to one to make it faster
            //selecting random items+-
            for (int i = 0; i < numItemsToOrder; i++) {
                int menuIndex = (int) (Math.random() * (menu.size()));
                Order order = systemInterface.createOrder(menu.get(menuIndex), party);
                party.addOrder(order);
            }
            
        }
        notifyObservers(" ------------------------------");
    }


    public void toggleRunning() {
        running = !running;
    }

    private void advanceClock() {
        clockTick++;

    }

    public void setSleepTime(int time) {
        this.sleepTime = time;
    }

    public int getSleepTime() {
        return this.sleepTime;
    }
    
    public void createComment(){
    	int i = (int)(Math.random()*(this.names.length));
    	String name = names[i];
    	i = (int)(Math.random()*(this.comments.length));
    	String comment = comments[i];
    	notifyObservers("Comment " + name + " added: " + comment);
    	systemInterface.createComment(name, comment);
    }
}
